package ClientPackage;

import java.net.DatagramSocket;
import java.net.InetAddress;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.util.Arrays;

import javax.crypto.SecretKey;

import SecureUtil.DiffieHellman;
import SecureUtil.Packer;
import SecureUtil.RSAHelper;
import SecureUtil.UDPHelper;
import SecureUtil.Packer.DataType;


/**
 * This thread will start only after the client received a cookie that matches with
 * ip, port and random number.
 * It will eventually print out the message that he received.
 * 
 * 
 * @author Zhisheng
 *
 */
public class ClientAuthenticateThread implements Runnable {

	
	private DatagramSocket _socketForAuthenticate;
	private PrivateKey _clientPrivKey;
	private ClientInfo _receiveClientInfo;
	
	private InetAddress _fromIp;
	private Integer _fromPort;
	
	public ClientAuthenticateThread(ClientInfo receiveClientInfo, DatagramSocket newSocketForAuthenticate, PrivateKey clientPrivKey, InetAddress fromip, Integer fromport) {
		
		_receiveClientInfo = receiveClientInfo;
		_socketForAuthenticate = newSocketForAuthenticate;
		_clientPrivKey = clientPrivKey;
		_fromIp = fromip;
		_fromPort = fromport;
		// TODO Auto-generated constructor stub
	}	

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Packer packer = new Packer();
			UDPHelper udphelper = new UDPHelper(_socketForAuthenticate);		
			DiffieHellman dh = new DiffieHellman();			
			byte[] firstHalfDH;					
			firstHalfDH = dh.generateFirstHalf();	//generate DH first half								
			SecureRandom random = new SecureRandom();
			byte [] N1 = new byte[20];
			random.nextBytes(N1);	 //generate N1    
			
			RSAHelper rsaHelper = new RSAHelper();
			rsaHelper.pubKey = _receiveClientInfo._pubkey;
			byte [] encryptedN1 = rsaHelper.RSAEncrypt(N1);
			
			packer.Reset();				    
			packer.addElement(DataType.BYTES, firstHalfDH);
	    	packer.addElement(DataType.BYTES, encryptedN1);
	   
	    	//1) send back gb mod p, {N1}A
	    	udphelper.sendBytes(packer.pack(), _fromIp, _fromPort);
	    	packer.Reset();
	    	System.out.println("send back (gb mod p, {N1}A). ");
	    	
	    	
	    	//2) waiting for receive      ga mod p, {A,N2,N1}KB
	    	byte[] receivedEncrypted5th = udphelper.receiveBytes(10000);	   
	    	System.out.println("received ( ga mod p, {A,N2,N1}KB) ");
	    	
	    	//packer.addElement(DataType.BYTES, secondHalfDH);
	    	packer.unpack(receivedEncrypted5th);
	    	
	    	//get second half DH
	    	byte[] secondHalfDH = (byte[]) packer.getNext(Packer.DataType.BYTES);
	    	//get encrypted {A,N2,N1}KB
	    	byte[] encrypted5th = (byte[]) packer.getNext(Packer.DataType.BYTES);
	    	packer.Reset();
	    	
	    	
			rsaHelper.privKey = _clientPrivKey;	
			//get decrypted {A,N2, [N1]A}
	    	byte [] decrypted5th = rsaHelper.RSADecrypt(encrypted5th);	    	
	    	
	    	packer.unpack(decrypted5th);
	    	
	    	String userName = (String)packer.getNext(Packer.DataType.STRING);
	    	
	    	byte[] N2 = (byte[]) packer.getNext(Packer.DataType.BYTES);
	    	byte [] receivedN1 = (byte[]) packer.getNext(Packer.DataType.BYTES);    	
	    
			packer.Reset();
			
			ClientInfo talkToClient = Client.getInstance().SearchClientInfo(userName);
			
			if (talkToClient == null)
				throw new Exception("no this user name found");
			//because he is the one who send first, then he is client
			//Now he gets the Key
			SecretKey clientSharedkey = dh.ClientSharedKey(secondHalfDH);
			
			rsaHelper.pubKey = talkToClient._pubkey;
			if(!(Arrays.equals(N1, receivedN1)))
				throw new Exception("N1 Verify fails.");
			
			byte[] finalBytes = dh.encryptDH(N2, clientSharedkey);
			
			
			//3) send  encrypted bytes: Ka{N2}
			udphelper.sendBytes(finalBytes, _fromIp, _fromPort);
			System.out.println("send back Ka{N2}. ");
			
			//4) wait for message to receive
			byte[] receivedEncryptedMessage = udphelper.receiveBytes(10000, clientSharedkey);			
			byte[] recoveredMessage = dh.decryptDH(receivedEncryptedMessage, clientSharedkey);			
			System.out.println("\nReceived Message from "+userName+": "+new String(recoveredMessage)+"\n");
			System.out.println("Input a command(List, Send or Logout): ");

			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

}
